package org.cosmact.designer.xml.transfer.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.cosmact.common.utils.Utilities;
import org.cosmact.designer.xml.transfer.IVxmlTransfer;
import org.cosmact.designer.xml.utils.XmlUtil;
import org.cosmact.vxml.model.Dialog;
import org.cosmact.vxml.model.DialogConnection;
import org.cosmact.vxml.model.DialogConnectionType;
import org.cosmact.vxml.model.DialogType;
import org.cosmact.vxml.model.DialogXmlContent;
import org.cosmact.vxml.model.Document;
import org.cosmact.vxml.model.EConnectionType;
import org.cosmact.vxml.model.EDialogType;
import org.cosmact.vxml.model.EVoiceXmlNames;
import org.cosmact.vxml.model.ModelFactory;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.eclipse.emf.common.util.EList;

/**
 * 
 * 
 *@author YeXiaowei 234aini@gmail.com
 * 
 */
public class VxmlTransfer implements IVxmlTransfer {

    Logger logger = Logger.getLogger(VxmlTransfer.class);

    @SuppressWarnings("unchecked")
    @Override
    public Document parseVxml(InputStream stream) {
	SAXReader reader = new SAXReader();
	try {
	    Document document = ModelFactory.eINSTANCE.createDocument();
	    org.dom4j.Document xmlDocument = reader.read(stream);
	    Element rootElement = xmlDocument.getRootElement();

	    // The first dialog which is not a Catch dialog should be setted as
	    // the start dialog
	    boolean startFound = false;
	    for (Iterator iterator = rootElement.elementIterator(); iterator
		    .hasNext();) {
		Element element = (Element) iterator.next();
		Dialog dialog = convertDialogFromElement(element);
		if (dialog != null) {
		    document.addDialog(dialog);
		    if (!startFound) {
			if (dialog.getDialogType().getType() != EDialogType.CATCH) {
			    dialog.setStartDialog(true);
			    startFound = true;
			}
		    }
		}
	    }
	    // Compute the connections between all dialogs.
	    computeConnections(document);
	    return document;
	} catch (DocumentException e) {
	    logger.error(e);
	    return null;
	} finally {
	    try {
		stream.close();
	    } catch (IOException e) {
	    }
	}
    }

    private void computeConnections(Document document) {
	EList<Dialog> dialogs = document.getDialogs();
	if (dialogs.isEmpty())
	    return;
	for (Dialog dialog : dialogs) {
	    try {
		org.dom4j.Document doc = XmlUtil.parse(dialog.getXmlContent()
			.getContent());
		findConnections(dialogs, dialog, doc, EConnectionType.GOTO);
		findConnections(dialogs, dialog, doc, EConnectionType.MENU_ITEM);
	    } catch (DocumentException e) {
		logger.error(e);
	    }
	}
    }

    @SuppressWarnings("unchecked")
    private void findConnections(EList<Dialog> dialogs, Dialog dialog,
	    org.dom4j.Document doc, EConnectionType type) {
	ModelFactory modelFactory = ModelFactory.eINSTANCE;
	String key = null;

	if (type.equals(EConnectionType.GOTO)) {
	    key = "goto";
	} else if (type.equals(EConnectionType.MENU_ITEM)) {
	    key = "choice";// choice
	} else {
	    return;
	}
	/**
	 * @see <a
	 *      href="http://www.cnblogs.com/PunkChen/archive/2008/04/02/1134304.html">xpath
	 *      usage with namespace by dom4j</a>
	 */
	String path = "//*[local-name()='%s' and namespace-uri()='http://www.w3.org/2001/vxml']";
	List gotos = doc.selectNodes(String.format(path, key));
	for (Iterator iterator = gotos.iterator(); iterator.hasNext();) {
	    Element element = (Element) iterator.next();
	    String destDialogID = element.attributeValue("next");
	    // destDialogID have value like #menu_id
	    if (Utilities.isNullOrEmpty(destDialogID)) {
		logger.warn(String.format(
			"Dialog %s have a %s element with no destnation.",
			dialog.getId(), key));
	    }
	    Dialog destnationDialog = null;
	    for (Dialog dia : dialogs) {
		if (("#" + dia.getId()).equals(destDialogID)) {
		    destnationDialog = dia;
		    break;
		}
	    }
	    if (destnationDialog == null) {
		logger
			.warn(String
				.format(
					"Dialog %s have a %s element with next value %s. The destnation is not exist in the follow.",
					dialog.getId(), key, destDialogID));
	    } else {
		DialogConnection connection = modelFactory
			.createDialogConnection();
		DialogConnectionType connectionType = modelFactory
			.createDialogConnectionType();
		connectionType.setType(type);
		connection.setConnectionType(connectionType);
		connection.setSource(dialog);
		connection.setTarget(destnationDialog);
		connection.setIsConnect(true);

		dialog.addConnection(connection);
		destnationDialog.addConnection(connection);
	    }

	}
    }

    private Dialog convertDialogFromElement(Element element) {
	if (element == null) {
	    return null;
	}

	EDialogType dialogType = getDialogType(element);
	if (dialogType == null) {
	    return null;
	}

	ModelFactory modelFactory = ModelFactory.eINSTANCE;

	Dialog vxmlDialog = modelFactory.createDialog();
	vxmlDialog
		.setId(element.attributeValue(EVoiceXmlNames.ID.getLiteral()));
	DialogXmlContent dialogContent = modelFactory.createDialogXmlContent();
	dialogContent.setContent(element.asXML());
	dialogContent.setDialog(vxmlDialog);
	vxmlDialog.setXmlContent(dialogContent);
	DialogType actualDialogType = modelFactory.createDialogType();
	actualDialogType.setType(dialogType);
	vxmlDialog.setDialogType(actualDialogType);

	return vxmlDialog;
    }

    /**
     * If element name is not in Enum DialogType, it is not a dialog
     * 
     * @param element
     * @return
     */
    private EDialogType getDialogType(Element element) {
	return EDialogType.get(element.getName());
    }
}
